package pl.edu.agh.ssm.monitor.data;

import java.net.InetAddress;
import java.util.LinkedList;
import java.util.List;

import pl.edu.agh.ssm.monitor.SessionMonitorApp;

/**
 * Contains session information like session description, session participants,
 * type of session and media streams description
 * 
 * @author Tomasz Jadczyk
 * 
 */
public class Session implements SessionData {
	// TODO Clean this class
	private String sessionID;
	private SessionType sessionType;
	private List<SessionNode> sessionParticipants;
	private List<SessionConnection> connectionsBetweenParticipants;
	private String sessionDesc;

	private SessionDataContainer<SessionNode> nodes;
	private SessionDataContainer<SessionConnection> connections;
	private SessionDescription sessionDescription;

	private long noParticipantsTimeStamp;
	private long lastSDPTime = -1;

	public Session(String sessionID) {
		this.sessionID = sessionID;
		sessionParticipants = new LinkedList<SessionNode>();
		connectionsBetweenParticipants = new LinkedList<SessionConnection>();
		nodes = SessionMonitorApp.getApplication().getManager()
				.createContainer(SessionNode.class);
		connections = SessionMonitorApp.getApplication().getManager()
				.createContainer(SessionConnection.class);
	}

	/*
	 * ----------------------------------------------------------------------
	 * Node operations
	 * ----------------------------------------------------------------------
	 */
	public void addNode(SessionNode node) {
		System.out.println("ADD_NODE: " + node.getId() + " To SESSION: " + getId());
		nodes.add(node);
		sessionParticipants.add(node);
	}

	public void removeNode(SessionNode node) {
		// first remove all connections
		List<SessionConnection> srConnections = getConnectionsFromSource(node);
		List<SessionConnection> recvConnections = getConnectionsToReceiver(node);
		connectionsBetweenParticipants.removeAll(srConnections);
		recvConnections.removeAll(recvConnections);
		sessionParticipants.remove(node);
		nodes.remove(node);
	}

	public void updateNode(SessionNode node) {
		// System.out.println("SESSION Update node " + node.getSsrc());
		nodes.update(node);
	}

	public SessionNode getNode(long ssrc) {
		for (SessionNode node : sessionParticipants) {
			if (node.getSsrc() == ssrc) {
				return node;
			}
		}
		return null;
	}

	// public SessionNode getNode(InetAddress address) {
	// for (SessionNode node : sessionParticipants) {
	// if (node.getAddress().getHostAddress().equalsIgnoreCase(
	// address.getHostAddress())) {
	// return node;
	// }
	// }
	// return null;
	// }

	public List<SessionNode> getSessionParticipants() {
		return sessionParticipants;
	}

	/*
	 * ----------------------------------------------------------------------
	 * Session type and desc operations
	 * ----------------------------------------------------------------------
	 */
	public SessionType getSessionType() {
		return sessionType;
	}

	public void setSessionType(SessionType sessionType) {
		this.sessionType = sessionType;
	}

	public String getSessionDesc() {
		return sessionDesc;
	}

	public void setSessionDesc(String sessionDesc) {
		this.sessionDesc = sessionDesc;
	}

	public String getSessionID() {
		return sessionID;
	}

	/*
	 * ----------------------------------------------------------------------
	 * Manipulate connections operations
	 * ----------------------------------------------------------------------
	 */

	// public void addConnection(SessionNode sender, SessionNode receiver) {
	// SessionConnection connection = new SessionConnection(sender, receiver,
	// this.sessionID);
	// addConnection(connection);
	// }
	public void addConnection(SessionConnection connection) {
		connections.add(connection);
		connectionsBetweenParticipants.add(connection);
	}

	public void updateConnection(SessionConnection connection) {
		connections.update(connection);
	}

	public void removeConnection(SessionNode sender, SessionNode receiver) {
		SessionConnection cn = null;
		for (SessionConnection conn : connectionsBetweenParticipants) {
			if (conn.getSender() == sender && conn.getReceiver() == receiver) {
				cn = conn;
				break;
			}
		}
		if (cn != null) {
			connectionsBetweenParticipants.remove(cn);
		}
	}

	public List<SessionConnection> getConnectionsFromSource(SessionNode source) {
		// System.out.println("Source ssrc:" + source.getSsrc());
		List<SessionConnection> sourceConnections = new LinkedList<SessionConnection>();
		for (SessionConnection conn : connectionsBetweenParticipants) {
			// System.out.println("Conn: " +
			// conn.getConnectionAddress().getHostAddress() + ":"
			// + conn.getConnectionPort() + ", Sender: " +
			// conn.getSender().getSsrc());
			if (conn.getSender() == source) {
				sourceConnections.add(conn);
			}
		}
//		System.out.println("Return " + sourceConnections.size()
//				+ " connections");
		return sourceConnections;
	}

	public List<SessionConnection> getConnectionsToReceiver(SessionNode receiver) {
		List<SessionConnection> receiverConnections = new LinkedList<SessionConnection>();
		for (SessionConnection conn : connectionsBetweenParticipants) {
			if (conn.getReceiver() == receiver) {
				receiverConnections.add(conn);
			}
		}
		return receiverConnections;
	}

	public List<SessionConnection> getSpecifiedConnection(SessionNode sender,
			InetAddress connectionAddress, int connectionPort) {
		List<SessionConnection> specifiedConnections = new LinkedList<SessionConnection>();
		for (SessionConnection conn : getConnectionsFromSource(sender)) {

			if (conn.getConnectionAddress().getHostAddress().equalsIgnoreCase(
					connectionAddress.getHostAddress())
					&& conn.getConnectionPort() == connectionPort) {
				specifiedConnections.add(conn);
			} else {
//				System.out.println("Connection: " + conn.getSender().getSsrc()
//						+ ":" + conn.getConnectionAddress().getHostAddress()
//						+ ":" + conn.getConnectionPort());
//				System.out.println("Sender: " + sender.getSsrc() + ", Address:"
//						+ connectionAddress.getHostAddress() + ":"
//						+ connectionPort);
			}
		}
		return specifiedConnections;
	}

	public SessionConnection getConnection(SessionNode source,
			SessionNode receiver, InetAddress connectionAddress, int port) {
		// Connection cn = null;
		// System.out.println("Get connection: Sender:" + source.getSsrc()
		// + ", Recv: " +receiver.getSsrc()
		// + ", Inet: " + connectionAddress.getHostAddress()
		// + ", Port: " + port);
		for (SessionConnection conn : connectionsBetweenParticipants) {

			if (conn.getSender() == source
					&& conn.getReceiver() == receiver
					&& conn.getConnectionAddress().getHostAddress()
							.equalsIgnoreCase(
									connectionAddress.getHostAddress())
					&& conn.getConnectionPort() == port)
				return conn;
		}
		return null;
	}

	public List<SessionConnection> getAllConnectionsBetweenNodes(
			SessionNode source, SessionNode receiver) {
		List<SessionConnection> result = new LinkedList<SessionConnection>();
		for (SessionConnection conn : connectionsBetweenParticipants) {
			if (conn.getSender() == source && conn.getReceiver() == receiver)
				result.add(conn);
		}
		return result;

	}

	@Override
	public String getId() {
		return sessionID;
	}

	public SessionDataContainer<SessionNode> getNodes() {
		return nodes;
	}

	public void setNodesContainer(SessionDataContainer<SessionNode> nodes) {
		this.nodes = nodes;
	}

	public SessionDataContainer<SessionConnection> getConnections() {
		return connections;
	}

	public void setConnectionsContainer(
			SessionDataContainer<SessionConnection> connections) {
		this.connections = connections;
	}

	public SessionDescription getSessionDescription() {
		return sessionDescription;
	}

	public void setSessionDescription(SessionDescription sessionDescription) {
		this.sessionDescription = sessionDescription;
	}

	@Override
	public String toString() {
		return getSessionDesc();
	}

	public long getNoParticipantsTimeStamp() {
		return noParticipantsTimeStamp;
	}

	public void setNoParticipantsTimeStamp(long noParticipantsTimeStamp) {
		this.noParticipantsTimeStamp = noParticipantsTimeStamp;
	}

	public long getLastSDPTime() {
		return lastSDPTime;
	}

	public void setLastSDPTime(long lastSDPTime) {
		this.lastSDPTime = lastSDPTime;
	}

}
